const crypto = require('crypto');
const { Room, User, Message } = require('./models');

/**
 * RoomManager handles all room-related operations
 */
class RoomManager {
    constructor() {
        this.rooms = new Map(); // roomId -> Room
        this.users = new Map(); // userId -> User  
        this.inviteCodes = new Map(); // inviteCode -> roomId
        this.websockets = new Map(); // websocket -> userId
        this.userToRoom = new Map(); // userId -> roomId
    }

    /**
     * Create a new chat room
     */
    createRoom(name, password = null, maxUsers = 50) {
        if (!name || name.trim().length === 0) {
            throw new Error('Room name is required');
        }

        if (name.length > 50) {
            throw new Error('Room name is too long (max 50 characters)');
        }

        const roomId = Room.generateRoomId();
        const room = new Room(roomId, name.trim(), password, maxUsers);
        
        this.rooms.set(roomId, room);
        this.inviteCodes.set(room.inviteCode, roomId);
        
        console.log(`Room created: ${room.name} (ID: ${roomId})`);
        return room;
    }

    /**
     * Get room by ID
     */
    getRoom(roomId) {
        return this.rooms.get(roomId);
    }

    /**
     * Get room by invite code
     */
    getRoomByInviteCode(inviteCode) {
        const roomId = this.inviteCodes.get(inviteCode);
        return roomId ? this.rooms.get(roomId) : null;
    }

    /**
     * Join a room with user details
     */
    joinRoom(roomId, nickname, password = null, websocket = null) {
        const room = this.getRoom(roomId);
        if (!room) {
            throw new Error('Room not found');
        }

        if (!nickname || nickname.trim().length === 0) {
            throw new Error('Nickname is required');
        }

        if (nickname.length > 30) {
            throw new Error('Nickname is too long (max 30 characters)');
        }

        if (!room.validatePassword(password)) {
            throw new Error('Invalid room password');
        }

        if (room.members.size >= room.maxUsers) {
            throw new Error('Room is full');
        }

        // Check if nickname is already taken in this room
        const existingUser = Array.from(room.members.values())
            .find(user => user.nickname.toLowerCase() === nickname.trim().toLowerCase());
        
        if (existingUser) {
            throw new Error('Nickname is already taken in this room');
        }

        // Generate unique user ID
        const userId = crypto.randomBytes(8).toString('hex');
        const user = new User(userId, nickname.trim(), websocket);
        
        // Add user to room
        room.addUser(user);
        
        // Update mappings
        this.users.set(userId, user);
        this.userToRoom.set(userId, roomId);
        if (websocket) {
            this.websockets.set(websocket, userId);
        }

        console.log(`User ${nickname} joined room ${room.name}`);
        return { user, room };
    }

    /**
     * Leave a room
     */
    leaveRoom(userId) {
        const roomId = this.userToRoom.get(userId);
        if (!roomId) {
            return null;
        }

        const room = this.getRoom(roomId);
        const user = this.users.get(userId);
        
        if (room && user) {
            room.removeUser(userId);
            
            // Clean up mappings
            this.users.delete(userId);
            this.userToRoom.delete(userId);
            if (user.websocket) {
                this.websockets.delete(user.websocket);
            }

            console.log(`User ${user.nickname} left room ${room.name}`);

            // Clean up empty rooms after a delay
            setTimeout(() => {
                this.cleanupEmptyRoom(roomId);
            }, 5000);

            return { user, room };
        }

        return null;
    }

    /**
     * Handle user disconnection
     */
    handleDisconnect(websocket) {
        const userId = this.websockets.get(websocket);
        if (userId) {
            const user = this.users.get(userId);
            if (user) {
                user.setOffline();
                // Don't immediately remove - give them a chance to reconnect
                setTimeout(() => {
                    if (user && !user.isOnline) {
                        this.leaveRoom(userId);
                    }
                }, 30000); // 30 second grace period
            }
        }
    }

    /**
     * Reconnect user to room
     */
    reconnectUser(userId, websocket) {
        const user = this.users.get(userId);
        if (user) {
            user.websocket = websocket;
            user.isOnline = true;
            user.updateActivity();
            this.websockets.set(websocket, userId);
            return user;
        }
        return null;
    }

    /**
     * Get user by websocket
     */
    getUserByWebSocket(websocket) {
        const userId = this.websockets.get(websocket);
        return userId ? this.users.get(userId) : null;
    }

    /**
     * Get user's current room
     */
    getUserRoom(userId) {
        const roomId = this.userToRoom.get(userId);
        return roomId ? this.getRoom(roomId) : null;
    }

    /**
     * Add message to user's room
     */
    addMessage(userId, content, type = 'text') {
        const room = this.getUserRoom(userId);
        const user = this.users.get(userId);
        
        if (!room || !user) {
            throw new Error('User not in any room');
        }

        if (!content || content.trim().length === 0) {
            throw new Error('Message content is required');
        }

        if (content.length > 1000) {
            throw new Error('Message is too long (max 1000 characters)');
        }

        const message = new Message(userId, content.trim(), type);
        room.addMessage(message);
        user.updateActivity();

        return { message, room };
    }

    /**
     * Get all users in a room
     */
    getRoomUsers(roomId) {
        const room = this.getRoom(roomId);
        return room ? room.getOnlineUsers() : [];
    }

    /**
     * Broadcast message to all users in a room
     */
    broadcastToRoom(roomId, message, excludeUserId = null) {
        const room = this.getRoom(roomId);
        if (!room) {
            return;
        }

        const users = room.getOnlineUsers();
        const results = [];

        for (const user of users) {
            if (excludeUserId && user.id === excludeUserId) {
                continue;
            }

            if (user.websocket && user.websocket.readyState === 1) { // WebSocket.OPEN
                try {
                    user.websocket.send(JSON.stringify(message));
                    results.push({ userId: user.id, success: true });
                } catch (error) {
                    console.error(`Failed to send message to user ${user.nickname}:`, error);
                    results.push({ userId: user.id, success: false, error: error.message });
                }
            }
        }

        return results;
    }

    /**
     * Validate invite code
     */
    validateInviteCode(inviteCode) {
        const room = this.getRoomByInviteCode(inviteCode);
        return {
            valid: !!room,
            room: room ? room.toJSON() : null
        };
    }

    /**
     * Clean up empty rooms
     */
    cleanupEmptyRoom(roomId) {
        const room = this.getRoom(roomId);
        if (room && room.isEmpty()) {
            // Remove from all mappings
            this.rooms.delete(roomId);
            this.inviteCodes.delete(room.inviteCode);
            
            console.log(`Cleaned up empty room: ${room.name}`);
        }
    }

    /**
     * Get server statistics
     */
    getStats() {
        const totalRooms = this.rooms.size;
        const totalUsers = this.users.size;
        const activeConnections = this.websockets.size;
        
        const roomDetails = Array.from(this.rooms.values()).map(room => ({
            id: room.id,
            name: room.name,
            memberCount: room.getOnlineUsers().length,
            messageCount: room.messages.length,
            createdAt: room.createdAt
        }));

        return {
            totalRooms,
            totalUsers,
            activeConnections,
            rooms: roomDetails,
            uptime: process.uptime()
        };
    }

    /**
     * Periodic cleanup of inactive users and empty rooms
     */
    performMaintenance() {
        const now = new Date();
        const inactiveThreshold = 24 * 60 * 60 * 1000; // 24 hours

        // Clean up inactive users
        for (const [userId, user] of this.users) {
            if (!user.isOnline && (now - user.lastActivity) > inactiveThreshold) {
                this.leaveRoom(userId);
            }
        }

        // Clean up empty rooms
        for (const [roomId, room] of this.rooms) {
            if (room.isEmpty()) {
                this.cleanupEmptyRoom(roomId);
            }
        }

        console.log('Maintenance completed:', this.getStats());
    }
}

module.exports = RoomManager;